//=============================================================================
//
//		Copyright (C) 1995 by Paul S. McCarthy and Eric Sunshine.
//				Written by Paul S. McCarthy and Eric Sunshine.
//							All Rights Reserved.
//
//		This notice may not be removed from this source code.
//
//		This object is included in the MiscKit by permission from the authors
//		and its use is governed by the MiscKit license, found in the file
//		"License.rtf" in the MiscKit distribution.	Please refer to that file
//		for a list of all applicable permissions and restrictions.
//		
//=============================================================================
//-----------------------------------------------------------------------------
// MiscIntList.M
//
//		A dynamically sizable list of integers.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id: MiscIntList.M,v 1.5 95/10/03 05:08:16 zarnuk Exp $
// $Log:		MiscIntList.M,v $
//	Revision 1.5  95/10/03	05:08:16  zarnuk
//	Fixed compiler warning.
//	
//	Revision 1.4  95/10/03	04:57:41  sunshine
//	Fixed -init so Storage is allocated from [self zone].  Added copyFromZone:,
//	initFromString:, and addIntList:.
//	
//	Revision 1.3  95/10/03	03:56:44  sunshine
//	Fixed bug where -readFromString: couldn't parse negative numbers.
//	
//	Revision 1.2  95/09/30	19:01:05  zarnuk
//	Added sting read/write methods.	 Added rawData method.
//-----------------------------------------------------------------------------
#import <misckit/MiscIntList.h>

extern "Objective-C" {
#import <objc/Storage.h>
}

extern "C" {
#include <stdlib.h>
#include <stdio.h>
}

//-----------------------------------------------------------------------------
// std_cmp
//-----------------------------------------------------------------------------
static int std_cmp( void*, int x, int y )
	{
	if (x < y)
		return -1;
	if (y > x)
		return 1;
	return 0;
	}


//-----------------------------------------------------------------------------
// qsort_cmp
//-----------------------------------------------------------------------------
static MiscIntListCompareFunc cmp_func = 0;
static void* cmp_data = 0;
static int qsort_cmp( void const* px, void const* py )
	{
	return (*cmp_func)( cmp_data, *((int const*)px), *((int const*)py) );
	}


@implementation MiscIntList

//-----------------------------------------------------------------------------
// init
//-----------------------------------------------------------------------------
- init
	{
	[super init];
	array = [[Storage allocFromZone: [self zone]]
				initCount:0 elementSize:sizeof(int) description:"i"];
	return self;
	}


//-----------------------------------------------------------------------------
// initFromString:
//-----------------------------------------------------------------------------
- initFromString: (char const*) s
	{
	[self init];
	[self readFromString:s];
	return self;
	}


//-----------------------------------------------------------------------------
// free
//-----------------------------------------------------------------------------
- free
	{
	[array free];
	return [super free];
	}


//-----------------------------------------------------------------------------
// copyFromZone:
//-----------------------------------------------------------------------------
- copyFromZone: (NXZone*) zone
	{
	MiscIntList* clone = [super copyFromZone:zone];
	clone->array = [array copyFromZone:zone];
	return clone;
	}


//-----------------------------------------------------------------------------
// empty
//-----------------------------------------------------------------------------
- empty
	{
	[array empty];
	return self;
	}


//-----------------------------------------------------------------------------
// count
//-----------------------------------------------------------------------------
- (unsigned int) count
	{
	return [array count];
	}


//-----------------------------------------------------------------------------
// intAt:
//-----------------------------------------------------------------------------
- (int) intAt: (unsigned int) pos
	{
	return (pos < [self count] ? (*(int*)[array elementAt:pos]) : 0);
	}


//-----------------------------------------------------------------------------
// addInt:
//-----------------------------------------------------------------------------
- (void) addInt: (int) value
	{
	[self insertInt: value at: [self count]];
	}


//-----------------------------------------------------------------------------
// addIntList:
//-----------------------------------------------------------------------------
- (void) addIntList: (MiscIntList*) list
	{
	if (list != 0)
		{
		unsigned int const lim = [list count];
		for (unsigned int i = 0; i < lim; i++)
			[self addInt: [list intAt:i]];
		}
	}


//-----------------------------------------------------------------------------
// insertInt:at:
//-----------------------------------------------------------------------------
- (void) insertInt: (int) value at: (unsigned int) pos
	{
	[array insertElement: &value at: pos];
	}


//-----------------------------------------------------------------------------
// removeIntAt:
//-----------------------------------------------------------------------------
- (void) removeIntAt: (unsigned int) pos
	{
	[array removeElementAt: pos];
	}


//-----------------------------------------------------------------------------
// replaceIntAt:with:
//-----------------------------------------------------------------------------
- (void) replaceIntAt: (unsigned int) pos with: (int) value
	{
	[array replaceElementAt:pos with:&value];
	}


//-----------------------------------------------------------------------------
// sortUsing:data:
//-----------------------------------------------------------------------------
- (void) sortUsing:(MiscIntListCompareFunc)func data:(void*)data
	{
	unsigned int const N = [self count];
	if (N > 1)
		{
		cmp_func = (func ? func : std_cmp);
		cmp_data = data;
		qsort( [array elementAt:0], N, sizeof(int), qsort_cmp );
		}
	}


//-----------------------------------------------------------------------------
// sort
//-----------------------------------------------------------------------------
- (void) sort
	{
	[self sortUsing:&std_cmp data:0];
	}


//-----------------------------------------------------------------------------
// append_char
//-----------------------------------------------------------------------------
static void append_char( char c, char*& buff, int& buff_size, 
						int& len, BOOL can_expand )
	{
	if (buff != 0)
		{
		if (len >= buff_size)
			if (!can_expand)
				buff = 0;
			else
				{
				buff_size <<= 1;		// Double the size.
				buff = (char*) realloc( buff, buff_size );
				}
		if (buff != 0)
			buff[ len++ ] = c;
		}
	}


//-----------------------------------------------------------------------------
// append_int
//-----------------------------------------------------------------------------
static void append_int( int x, char*& buff, int& buff_size, 
						int& len, BOOL can_expand )
	{
	char tmp[ 64 ];
	sprintf( tmp, "%d", x );
	for (char const* t = tmp;  *t != 0 && buff != 0;  t++)
		append_char( *t, buff, buff_size, len, can_expand );
	}


//-----------------------------------------------------------------------------
// - writeToString:size:canExpand:
//-----------------------------------------------------------------------------
- (char*) writeToString: (char*) buff size: (int) buff_size
		canExpand:(BOOL) can_expand
	{
	int const START_SIZE = 32;
	int i,x,len;
	int const lim = [self count];

	if ((buff == 0 || buff_size == 0) && can_expand)
		{
		buff = (char*) malloc( START_SIZE );
		buff_size = START_SIZE;
		}

	if (buff_size == 0)
		buff = 0;

	len = 0;
	for (i = 0;	 buff != 0 && i < lim;	i++)
		{
		x = [self intAt:i];
		if (i > 0)
			append_char( ' ', buff, buff_size, len, can_expand );
		append_int( x, buff, buff_size, len, can_expand );
		}

	append_char( '\0', buff, buff_size, len, can_expand );

	return buff;
	}


//-----------------------------------------------------------------------------
// - writeToString:size:
//-----------------------------------------------------------------------------
- (char*) writeToString: (char*) buff size: (int) buff_size
	{
	return [self writeToString:buff size:buff_size canExpand:NO];
	}


//-----------------------------------------------------------------------------
// writeToString
//-----------------------------------------------------------------------------
- (char*) writeToString
	{
	return [self writeToString:0 size:0 canExpand:YES];
	}


//-----------------------------------------------------------------------------
// readFromString:
//-----------------------------------------------------------------------------
- (int) readFromString: (char const*) s
	{
	[self empty];
	if (s != 0)
		{
		while (*s != 0)
			{
			while (*s != 0 && *s != '-' && (*s < '0' || '9' < *s))
				s++;

			BOOL is_neg = NO;
			if (*s == '-')
				{
				is_neg = YES;
				s++;
				}

			if ('0' <= *s && *s <= '9')
				{
				int x = 0;
				do	{
					x = x * 10 + *s - '0';
					s++;
					}
				while ('0' <= *s && *s <= '9');
				[self addInt: (is_neg ? -x : x)];
				}
			}
		}
	return [self count];
	}


//-----------------------------------------------------------------------------
// rawData
//-----------------------------------------------------------------------------
- (int const*) rawData
	{
	if ([self count] == 0)
		return 0;
	return (int const*) [array elementAt: 0];
	}

@end
